﻿using System.Collections.Concurrent;
using System.Text;
using System.Text.RegularExpressions;
using TrackSystem.Entities.Extensions;
using TrackSystem.IO.Utils;

namespace TrackSystem.IO.Formula
{
    /// <summary>
    /// 程序页处理
    /// </summary>
    public class PageAnalysis
    {
        /// <summary>
        /// 将多行程序转换为程序行
        /// </summary>
        /// <param name="expression"></param>
        /// <param name="sLine"></param>
        /// <returns></returns>
        public static ResultMessage PrePageToLine(string expression, out List<string> sLine)
        {
            // sLine初始化
            sLine = new List<string>();

            // 空字符串
            if (string.IsNullOrEmpty(expression))
            {
                throw new Exception("空行");
            }

            // 消除空格 \t \r等字符
            expression = expression.Replace(" ", "").Replace("\t", "").Replace("\r", "");

            ResultMessage result = new ResultMessage() { Message="", Result = true };

            string[] arItem = expression.Split('\n');
            foreach(var item in arItem)
            {
                if (string.IsNullOrEmpty(item))
                {
                    continue;
                }

                if (item.IndexOf("//") == 0)
                {
                    continue;
                }

                if (item.IndexOf(";") < 0)
                {
                    result.Message += $"{item}  -- 表达式结束缺少分号";
                    result.Result = false;
                    continue;
                }

                if (!IsBrackMatch(item))
                {
                    result.Message += $"{item}   --括号不匹配";
                    result.Result = false;
                    continue;
                }

                sLine.Add(item.Replace(";", "@"));
            }

            if (result.Result)
            {
                result.Message = "Success";
            }

            return result;
        }

        /// <summary>
        /// 判断表示左右括号是否匹配
        /// </summary>
        /// <param name="exp"></param>
        /// <returns></returns>
        public static bool IsBrackMatch(string exp)
        {
            int cur = 0;

            foreach (char ch in exp)
            {
                if (ch == '(')
                    cur++;

                if (ch == ')')
                    cur--;

                if (cur < 0)
                    return false;
            }

            return cur == 0;
        }

        /// <summary>
        /// 元素切割
        /// </summary>
        /// <param name="exp"></param>
        /// <param name="segments"></param>
        /// <param name="varName"></param>
        /// <returns></returns>
        public static ResultMessage ElementSegmentation(string exp, out List<string> segments, out string varName)
        {
            List<char> SPLIT_SYMBOL = new List<char> {'=', '+', '-', '*', '/', '?', ':',
                '<', '>', '=', '!', '(', ')', '|', '&', '@', '^', '%', ','};

            StringBuilder element = new StringBuilder();

            int current = 0;
            bool bEleStart = true;     //变量或常量的开始

            // 初始化分段
            segments = new List<string>();

            while (current < exp.Length)
            {
                if (SPLIT_SYMBOL.Contains(exp[current]))
                {
                    if (element.Length > 0)
                    {
                        segments.Add(element.ToString());
                        element.Clear();
                    }

                    segments.Add(exp[current].ToString());
                    switch (exp[current])
                    {
                        case '<':
                            if (exp[current + 1] == '>' || exp[current + 1] == '=')
                            {
                                segments[segments.Count - 1] += exp[current + 1];
                                current++;
                            }
                            break;

                        case '>':
                        case '!':
                            if (exp[current + 1] == '=')
                            {
                                segments[segments.Count - 1] += exp[current + 1];
                                current++;
                            }
                            break;

                        case '|':
                            if (exp[current + 1] == '|')
                            {
                                segments[segments.Count - 1] += exp[current + 1];
                                current++;
                            }
                            break;

                        case '&':
                            if (exp[current + 1] == '&')
                            {
                                segments[segments.Count - 1] += exp[current + 1];
                                current++;
                            }
                            break;

                        case '=':
                            if (exp[current + 1] == '=')
                            {
                                segments[segments.Count - 1] += exp[current + 1];
                                current++;
                            }
                            break;

                    }
                    bEleStart = true;
                }
                else
                {
                    // 如果操作数开始
                    if (bEleStart)
                    {
                        bEleStart = false;

                        element.Clear();
                        element.Append(exp[current]);
                    }
                    else
                    {
                        element.Append(exp[current]);
                    }
                }

                current++;
            }

            if (element.Length > 0)
            {
                segments.Add(element.ToString());
            }

            varName = "";

            // 表达式的长度不正确
            if (segments.Count < 3)
            {
                return new ResultMessage() { Message = "Operands less than 3", Result = false };
            }

            // 表达式必须有=号
            if (segments[1] != "=")
            {
                return new ResultMessage() { Message = "Incorrect expression", Result = false };
            }

            // 表达式输出必须是变量
            if (!IsVaild(segments[0]))
            {
                return new ResultMessage() { Message = $"{segments[0]}: Illegal variable name", Result = false };
            }

            varName = segments[0];

            segments.RemoveAt(0);
            segments.RemoveAt(0);
            return new ResultMessage() { Message = "Success", Result = true };
        }

        /// <summary>
        /// 是否合法
        /// </summary>
        /// <param name="name">名称</param>
        /// <returns></returns>
        public static bool IsVaild(string name)
        {
            string pattern = "^[a-zA-Z_][a-zA-Z0-9_]*$";
            return Regex.IsMatch(name, pattern);
        }

        /// <summary>
        /// 测试输入表达式段落是否正确
        /// </summary>
        /// <param name="expression"></param>
        /// <param name="varList"></param>
        /// <param name="lstOutVar"></param>
        /// <returns></returns>
        public static FormulaResult TestPageFormula(string expression, 
            Dictionary<string, DataTypeEnum> varList, 
            out Dictionary<string, DataTypeEnum> lstOutVar)
        {
            // 初始化
            lstOutVar = new Dictionary<string, DataTypeEnum>();

            // 预处理
            var preResult = PrePageToLine(expression, out var lstFormula);
            if (!preResult.Result)
                return FormulaResult.Fail(preResult.Message);

            //分段处理
            string msg = "";
            bool bError = false;
            foreach(var item in lstFormula)
            {
                var segResult = ElementSegmentation(item, out var lstSegment, out string regName);  // 分段
                if ( !varList.TryGetValue(regName, out var t) )
                {
                    return FormulaResult.Fail($"{item} --{regName} don't exist;\r\n");
                }

                if (segResult.Result)
                {
                    lstOutVar.TryAdd(regName, t);
                    Stack<ExprItem> stack = new Stack<ExprItem>();
                    var parResult = ClacuationFormula.Parse(lstSegment, stack, varList);  // 转换为中缀表达式
                    if (parResult.Success)
                    {
                        try
                        {
                            var op = ClacuationFormula.Evaluate(stack, true);   //计算
                            if ((t == DataTypeEnum.DOUBLE && op.DataType == DataTypeEnum.INTEGER ) 
                                ||  t == op.DataType)
                            {
                                msg += $"{regName} = {op}; \r\n";
                            }
                            else
                            {
                                msg += $"{regName} is {t}, return is {op.DataType};";
                                bError = true;
                            }
                        }
                        catch (Exception ex)
                        {
                            msg += $"{item}  -- {ex.Message};\r\n";
                            bError = true;
                        }
                    }
                    else  // 转换错误  ----------------------
                    {
                        msg += $"{item}  -- {parResult.Message};\r\n";
                        bError = true;
                    }
                }
                else  // 分段错误 ----------------
                {
                    msg += $"{item}  -- {segResult.Message};\r\n";
                    bError = true;
                }
            }

            return FormulaResult.New(!bError, msg);

        }
    }
}
